#!/usr/bin/env python
# encoding: utf-8
'''
@author: caopeng
@license: (C) Copyright 1999-2017, Fiberhome Telecommunication Technologies Co.,LTD.
@contact: gjzuo@fiberhome.com
@software: garner
@file: agent_host.py
@time: 2018/2/8 16:18
@desc:
'''

# coding:utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
from twisted.internet import reactor, task
from twisted.internet.protocol import Protocol, ClientFactory
import struct
from twisted.python import log
import sys
import json
import socket
import random
import fcntl
import commands

from client import API_1_0


log.startLogging(sys.stdout)


class EchoClient(Protocol):
    def __init__(self):
        self.command_func_dict = {
            101: self.handle_report_s,
            102: self.handle_single_chat_s,
            103: self.handle_group_chat_s,
            104: self.handle_broadcast_chat_s,
            105: self.handle_host_report_s
        }
        self.version = 0
        self.state = "VERIFY"
        self.phone_number = ""

    def connectionMade(self):
        log.msg("New connection", self.transport.getPeer())

    def dataReceived(self, data):
        log.msg('in dataReceived...')

        length, self.version, command_id = struct.unpack('!3I', data[:12])
        content = data[12:length]
        if self.state == "VERIFY" and command_id == 101:
            self.handle_report_s(content)
        elif self.state == "VERIFY" and command_id == 105:
            self.handle_host_report_s(content)
        else:
            self.handle_data(command_id, content)

    def handle_data(self, command_id, pack_data):
        self.command_func_dict[command_id](pack_data)

    def connectionLost(self, reason):
        log.msg("connection lost")

    def handle_report_s(self, pack_data):
        """
        接受上报的返回结果并处理
        """
        content = json.loads(pack_data)
        code = content.get('code')
        if code == 0:
            log.msg('上报成功，已保存数据到数据库')
        elif code == 1:
                log.msg("has same device already, please check ip correct!")
        else:
            log.msg('上报失败,请重新上报')
            reactor.stop()
        self.state = "Data"

    def handle_host_report_s(self, pack_data):
        """
        接受host上报的返回结果并处理
        """
        content = json.loads(pack_data)
        code = content.get('code')
        if code == 0:
            log.msg('host上报成功，vnf信息已保存到数据库')
        elif code == 1:
            log.msg("host report failed, please check vnfinfo correct!")
        else:
            log.msg('上报失败,请重新上报')
            reactor.stop()
        self.state = "Data"

    def handle_single_chat_s(self, pack_data):
        """
        接受单聊
        """
        content = json.loads(pack_data)
        chat_from = content.get('chat_from')
        chat_content = content.get('chat_content')
        log.msg("[单聊][%s]:%s" % (chat_from.encode('utf-8'), chat_content.encode('utf-8')))

    def handle_group_chat_s(self, pack_data):
        """
        接受组聊
        """
        content = json.loads(pack_data)
        chat_from = content.get('chat_from')
        chat_content = content.get('chat_content')
        log.msg("[组聊][%s]:%s" % (chat_from.encode('utf-8'), chat_content.encode('utf-8')))

    def handle_broadcast_chat_s(self, pack_data):
        """
        接受广播
        """
        content = json.loads(pack_data)
        chat_from = content.get('chat_from')
        chat_content = content.get('chat_content')
        log.msg("[群聊][%s]:%s" % (chat_from.encode('utf-8'), chat_content.encode('utf-8')))

    def vnf_online_report(self, ip, port):
        """
        设备上线上报，保存数据库
        """
        log.msg("ip:%s" %ip)
        log.msg("port:%s" %port)

        content = json.dumps(dict(ip=ip, port=port))
        self.send_data(content, 6)

    def host_report_vnfinfo(self, host_ip, vnf_info):
        """
        设备上线上报，保存数据库
        """

        log.msg("host_report_vnfinfo-->vnf_info = %s" %vnf_info)
        log.msg("host_report_vnfinfo-->host_ip = %s" %host_ip)

        content = json.dumps(dict(vnf_info=vnf_info, host_ip=host_ip)).encode('utf-8')
        self.send_data(content, 6)

    def send_single_chat(self, chat_from, chat_to, chat_content):
        """
        发送单聊内容
        """
        content = json.dumps(dict(chat_from=chat_from, chat_to=chat_to, chat_content=chat_content))
        self.send_data(content, 2)

    def send_group_chat(self, chat_from, chat_to, chat_content):
        """
        发送组聊内容
        """
        content = json.dumps(dict(chat_from=chat_from, chat_to=chat_to, chat_content=chat_content))
        self.send_data(content, 3)

    def send_broadcast_chat(self, chat_from, chat_content):
        """
        发送群聊内容
        """
        content = json.dumps(dict(chat_from=chat_from, chat_content=chat_content))
        self.send_data(content, 4)

    def send_data(self, send_content, command_id):
        """
        发送函数
        """
        length = 12 + len(send_content)
        version = self.version
        command_id = command_id
        header = [length, version, command_id]
        header_pack = struct.pack('!3I', *header)
        self.transport.write(header_pack + send_content)
        log.msg("send_data over....command_id-->%s" %command_id)

    def send_heartbeat(self):
        """
        发送心跳包
        """
        length = 12
        version = self.version
        command_id = 5
        header = [length, version, command_id]
        header_pack = struct.pack('!3I', *header)
        self.transport.write(header_pack)


class EchoClientFactory(ClientFactory):
    def __init__(self):
        self.p = EchoClient()

    def startedConnecting(self, connector):
        log.msg("Started to connect")

    def buildProtocol(self, addr):
        log.msg("Connected.")
        return self.p

    def clientConnectionFailed(self, connector, reason):
        log.msg("Lost connection. Reason:", reason)

    def clientConnectionLost(self, connector, reason):
        log.msg("Connection failed. Reason:", reason)

def get_ip(iface = 'br1'):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sockfd = sock.fileno()
    SIOCGIFADDR = 0x8915

    ifreq = struct.pack('16sH14s', iface, socket.AF_INET, '\x00'*14)
    try:
        res = fcntl.ioctl(sockfd, SIOCGIFADDR, ifreq)
    except:
        return None

    ip = struct.unpack('16sH2x4s8x', res)[2]
    return socket.inet_ntoa(ip)


QEMU_TYPE = "qemu-system-x86_64"
SPICE_PORT = "port"
MAC = "mac"
IP = "ip"
g_interface = ""

def get_vnf_info():
    (status, output) = commands.getstatusoutput('ps -ef | grep libvirt+')

    if status != 0:
        print("command ps -ef | grep libvirt+ exec error!")
        return False

    arr = output.split('\n')

    for psInfo in arr:
        if psInfo.find(QEMU_TYPE) == -1:
            index = arr.index(psInfo)
            arr.pop(index)

    vnfInfo = []
    for psInfo in arr:
        tmp_obj = {}
        ret1 = get_param_from_str(SPICE_PORT, psInfo, tmp_obj)
        ret2 = get_param_from_str(MAC, psInfo, tmp_obj)

        if ret1 == True:
            #通过MAC，利用arp获取虚拟机IP
            #仅需要获取MAC和port的对应关系，不需要获取IP，IP获取耗时太久,暂时不添加
            # arp_command = 'arp -a | grep "%s" | cut -f 2 -d " " | sed "s/[()]//g"' %tmp_obj[MAC]
            # (status, ip) = commands.getstatusoutput(arp_command)
            # if status == 0:
            #     if not ip:
            #         continue
            #     else:
            #         tmp_obj[IP] = ip


        if ret1 == True and ret2 == True:
            vnfInfo.append(tmp_obj)

    print vnfInfo
    return vnfInfo

def get_param_from_str(param, str, source_obj):
    if not str or not param:
        print("parameters not valid!")
        return False
    if str.find(QEMU_TYPE) == -1:
        print("not valid string, no qemu-system-x86_64")
        return False
    paramArray = str.split(",")
    find_result = 0
    for item in paramArray:
        if(item.find(param)) != -1:
            index = item.find(param)
            ret = item[index+len(param)+1:]
            source_obj[param] = ret
            find_result = 1
        else:
            continue
    if find_result == 1:
        return True
    else:
        return False

if __name__ == '__main__':
    cf = EchoClientFactory()
    #task_send_heartbeat = task.LoopingCall(cf.p.send_heartbeat)
    #task_send_heartbeat.start(60, now=False)

    host_ip = get_ip("br1")
    vnf_info = get_vnf_info()
    log.msg("1----->type(vnf_info)-->%s" %type(vnf_info))

    # vnf_info  = [
    #     {
    #         "ip": "192.168.122.185",
    #         "mac": "52:54:00:26:b0:14",
    #         "port": "5903"
    #     },
    #     {
    #         "ip": "192.168.122.130",
    #         "mac": "52:54:00:e7:d6:cb",
    #         "port": "5904"
    #     },
    #     {
    #         "ip": "192.168.122.63",
    #         "mac": "52:54:00:5e:e8:25",
    #         "port": "5905"
    #     },
    #     {
    #         "ip": "10.20.0.2",
    #         "mac": "52:54:00:c1:26:9e",
    #         "port": "5900"
    #     },
    #     {
    #         "ip": "",
    #         "mac": "52:54:00:0a:4c:4b",
    #         "port": "5901"
    #     },
    #     {
    #         "ip": "",
    #         "mac": "52:54:00:2b:5a:5e",
    #         "port": "5902"
    #     }
    # ]

    log.msg("2----->type(vnf_info)-->%s" %type(vnf_info))

    #reactor.callLater(1, cf.p.host_report_vnfinfo, host_ip, json.dumps(vnf_info).encode("UTF-8"))

    reactor.callLater(1, cf.p.host_report_vnfinfo, host_ip, vnf_info)

    task_send_heartbeat = task.LoopingCall(cf.p.host_report_vnfinfo, host_ip, vnf_info)
    task_send_heartbeat.start(5, now=False)

    reactor.connectTCP('192.168.200.200', 8124, cf)

    reactor.run()